﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Mecha.EdgeCompute.Framework
{
    /// <summary>
    /// 循环线程处理函数代理
    /// </summary>
    /// <param name="obj"></param>
    public delegate void LoppingProcedureHandler(object obj);
    /// <summary>
    /// 判定线程函数是否满足结束的函数代理
    /// </summary>
    /// <returns></returns>
    public delegate bool ThreadTerminateHandler();
    /// <summary>
    /// 主线程（界面线程）消息循环函数代理（Application.DoEvents）
    /// </summary>
    public delegate void MainThreadDoEventsHandler();
    /// <summary>
    /// 休眠函数代理
    /// </summary>
    public delegate void SleepHandler();


    public class ControlableThread
    {
        private bool m_ThreadRunning = false;
        private bool m_Paused = false;
        private object m_Lock = new object();
        private bool m_Condition = false;
        private LoppingProcedureHandler m_LoppingProcedure;
        private object m_LoppingProcedureParaObj;
        private ThreadTerminateHandler m_ThreadTerminateHandler;
        private MainThreadDoEventsHandler m_MainThreadDoEventsHandler;
        private SleepHandler m_SleepHandler;
        private int m_LoppingInterval = 15;
        private readonly int m_LoppingDefaultInterval = 15;

        public ControlableThread(LoppingProcedureHandler procedure,
                                 object paraObj,
                                 ThreadTerminateHandler isTterminated,
                                 int interval,
                                 MainThreadDoEventsHandler mainThreadDoEventsHandler
                                )
        {
            m_LoppingProcedure = procedure;
            m_LoppingProcedureParaObj = paraObj;
            m_ThreadTerminateHandler = isTterminated;
            m_LoppingInterval = interval;
            m_MainThreadDoEventsHandler = mainThreadDoEventsHandler;
            if (m_LoppingInterval <= m_LoppingDefaultInterval)
            {
                m_SleepHandler = SingleSleep;
            }
            else
            {
                m_SleepHandler = MultiSleep;
            }
        }
        public void Start()
        {
            if (m_ThreadRunning)
            {
                return;
            }
            lock (m_Lock)
            {
                if (m_ThreadRunning)
                {
                    return;
                }
                m_ThreadRunning = true;
                m_Condition = true;
            }

            Thread thread = new Thread(new ThreadStart(_LoppingProcedure));
            thread.IsBackground = true;
            thread.Start();
        }

        private void SingleSleep()
        {
            m_MainThreadDoEventsHandler?.Invoke();
            Thread.Sleep(m_LoppingInterval);
        }

        private void MultiSleep()
        {
            int n = m_LoppingInterval / m_LoppingDefaultInterval;
            while (n >= 0 && m_Condition && ((m_ThreadTerminateHandler != null && !m_ThreadTerminateHandler()) || m_ThreadTerminateHandler == null))
            {
                m_MainThreadDoEventsHandler?.Invoke();
                Thread.Sleep(m_LoppingDefaultInterval);
                n--;
            }
        }

        private void _LoppingProcedure()
        {
            while (m_Condition && ((m_ThreadTerminateHandler != null && !m_ThreadTerminateHandler()) || m_ThreadTerminateHandler == null))
            {
                if (!m_Paused)
                {
                    m_LoppingProcedure?.Invoke(m_LoppingProcedureParaObj);
                }
                m_SleepHandler();
            }
            m_ThreadRunning = false;
        }

        public void Stop()
        {
            if (!m_ThreadRunning)
            {
                return;
            }
            m_Condition = false;
            while (m_ThreadRunning)
            {
                m_MainThreadDoEventsHandler?.Invoke();
                Thread.Sleep(m_LoppingDefaultInterval);
            }
        }

        public void Pause()
        {
            m_Paused = true;
        }
        public void Resume()
        {
            m_Paused = false;
        }
    }
}
